﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizzCommInterfaces;
using log4net;


namespace BizzCore
{
    public class BizzSystem
    {
        private static readonly ILog auditLog =
                      LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private static BizzSystem _instance = null;
        private string status;
        

        protected BizzSystem()
        {
            auditLog.Info("BizzComm Server se ha iniciado");
            TraysHandler trays = TraysHandler.getInstance();
            trays.scanForInboxes();
            trays.scanForOutboxes();
            loadDataFromDB();
            status = "BizzComm Server se encuentra detenido";

        }

        private void loadDataFromDB()
        {
            try
            {
                TraysHandler trays = TraysHandler.getInstance();
                trays.restoreConcreteInboxesFromDB();
                trays.restoreConcreteOutboxesFromDB();
                RulesHandler.getInstance().restoreRulesFromDB();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw new Exception("No es posible conectar a la base de datos. Por favor revise la configuración");
                
            }
        }

        public static BizzSystem getInstance()
        {
            if (_instance == null)
                _instance = new BizzSystem();
            return _instance;
        }

        #region Inbox management

        public List<IInbox> getInboxProviders()
        {
            TraysHandler trays = TraysHandler.getInstance();
            trays.scanForInboxes();
            return trays.getInboxProviders();
        }

        public void setInboxFolder(string folder)
        {
            TraysHandler trays = TraysHandler.getInstance();
            trays.setInboxFolder(folder);
        }

        public List<IInbox> getConcreteInboxes()
        {
            TraysHandler trays = TraysHandler.getInstance();
            return trays.getConcreteInboxes();
        }

        public void setupConcreteInbox(IInbox inbox)
        {
            TraysHandler trays = TraysHandler.getInstance();
            IInbox newConcreteInbox = trays.createNewInboxInstance(inbox);
            newConcreteInbox.getConfigurationWindow();
            if (newConcreteInbox.isReady())
            {
                trays.addConcreteInbox(newConcreteInbox);
                trays.dbSaveConcreteInbox(newConcreteInbox);
                auditLog.Info("Se ha creado una nueva bandeja de entrada: " + newConcreteInbox.getConcreteInboxName());
            }
        }

        public void dbUpdateConcreteInbox(string inboxAttributes, IInbox updatedInbox)
        {
            try
            {
                TraysHandler.getInstance().dbUpdateConcreteInbox(inboxAttributes, updatedInbox);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw new Exception("Error al actualizar la bandeja de entrada");
            }
        }

        public void removeConcreteInbox(IInbox inbox)
        {
            try
            {
                TraysHandler.getInstance().removeConcreteInbox(inbox);
                auditLog.Info("Se ha eliminado una bandeja de entrada: " + inbox.getConcreteInboxName());
            }
            catch (Exception ex)
            {
                string errormsg = "Se produjo un error al eliminar la bandeja de entrada: " + inbox.getConcreteInboxName() + " " + ex.Message;
                auditLog.Info(errormsg);
                throw new Exception(errormsg);
            }
            
        }



        #endregion

        #region Outbox management

        public List<IOutbox> getOutboxProviders()
        {
            TraysHandler trays = TraysHandler.getInstance();
            trays.scanForOutboxes();
            return trays.getOutboxProviders();
        }

        public void setOutboxFolder(string folder)
        {
            TraysHandler trays = TraysHandler.getInstance();
            trays.setOutboxFolder(folder);
        }

        public List<IOutbox> getConcreteOutboxes()
        {
            TraysHandler trays = TraysHandler.getInstance();
            return trays.getConcreteOutbox();
        }

        public void setupConcreteOutbox(IOutbox outbox)
        {
            TraysHandler trays = TraysHandler.getInstance();
            IOutbox newConcreteOutbox = trays.createNewOutboxInstance(outbox);
            newConcreteOutbox.getConfigurationWindow();
            if (newConcreteOutbox.isReady())
            {
                trays.addConcreteOutbox(newConcreteOutbox);
                trays.dbSaveConcreteOutbox(newConcreteOutbox);
                auditLog.Info("Se ha creado una nueva bandeja de salida: " + newConcreteOutbox.getConcreteOutboxName());
            }

        }

        public void dbUpdateConcreteOutbox(string OutboxAttributes, IOutbox updatedOutbox)
        {
            try
            {
                TraysHandler.getInstance().dbUpdateConcreteOutbox(OutboxAttributes, updatedOutbox);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                throw new Exception("Error al actualizar la bandeja de salida");
            }
        }


        public void removeConcreteOutbox(IOutbox outbox)
        {
            try
            {
                TraysHandler.getInstance().removeConcreteOutbox(outbox);
                auditLog.Info("Se ha eliminado una bandeja de salida: " + outbox.getConcreteOutboxName());
            }
            catch (Exception ex)
            {
                string errormsg = "Se produjo un error al eliminar la bandeja de salida: " + outbox.getConcreteOutboxName() + " " + ex.Message;
                auditLog.Info(errormsg);
                throw new Exception(errormsg);
            }
        }
        #endregion

        #region Audit Logs

        public List<AuditLogEntity> getAuditLogs()
        {
            List<AuditLogEntity> logs = null;
            using (BizzCommContext ctx = new BizzCommContext())
            {
                var auditLogs = (from a in ctx.AuditLogs
                                 orderby a.Id descending
                                 select a).ToList<AuditLogEntity>();
                logs = auditLogs;
            }
            return logs;
        }

        #endregion

        #region Server

        public string getServerStatus()
        {
            return status;
        }

        public void startServer()
        {
            ProcessController.getInstance().startServer();
            status = "BizzComm Server se encuentra inciado";
        }

        public void stopServer()
        {
            ProcessController.getInstance().stopServer();
            status = "BizzComm Server se encuentra detenido";
        }

        #endregion

        #region RulesHandler

        public void addRule(string name, string field, Condition con, string expectedValue, IOutbox outbox)
        {
            RulesHandler.getInstance().addRule(name, field, con, expectedValue, outbox);
            auditLog.Info("Se ha creado una nueva regla: " + name + " " + field + " " + con.ToString() + " " + expectedValue + " -> " + outbox.ToString());
        }

        public void removeRule(Rule r)
        {
            auditLog.Info("Se ha eliminado una regla: " + r.ToString());
            RulesHandler.getInstance().removeRule(r);
        }

        public List<Rule> getRuleList()
        {
            return RulesHandler.getInstance().getRuleList();
        }

        public List<Condition> getConditions()
        {
            return RulesHandler.getInstance().getConditions();
        }

        #endregion

        #region ProcessController

        public void setRefreshInterval(int interval)
        {
            ProcessController.getInstance().setTimerInterval(interval);
        }

        public List<MessageEntity> getMessages(int max)
        {
            return ProcessController.getInstance().getMessages(max);
        }

        public List<MessageAttributeEntity> getMesageAttributes(int msgId)
        {
            return ProcessController.getInstance().getMesageAttributes(msgId);
        }

        public List<RuleMatchedEntity> getMessageRules(int msgId)
        {
            return ProcessController.getInstance().getMessageRules(msgId);
        }

        public List<OutboxEntity> getMessageOutBoxes(int msgId)
        {
            return ProcessController.getInstance().getMessageOutBoxes(msgId);
        }

        public List<MessageEntity> getMessages(List<string[]> filterAttrib, DateTime dateFrom, DateTime dateTo, string inbox)
        {
            return ProcessController.getInstance().getMessages(filterAttrib, dateFrom, dateTo,inbox);
        }

        
        #endregion
    }
}
